﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Security.Permissions;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows.Forms;
using ShowDependencies;
using ShowDependencies.Properties;
using Ait = Authorit.API;
using AuthoritExtensibility;
using AitConfig = Authorit.API.Configuration;

[assembly: SecurityPermission(SecurityAction.RequestMinimum, Execution = true)]
[assembly: CLSCompliant(true)]

namespace ShowDependencies
{
    public class PluginManager : IPlugin
    {
        #region Fields (17)

        private Ait.ApplicationContext appContext;
        private AitApplicationProxy appProxy;
        private Ait.Services.Authentication authService;
        private BackgroundWorker bgWorker = new BackgroundWorker();
        private static Regex htmlRegex;
        private Ait.Services.ObjectCommon objectCommon;
        private List<TreeNode> objectNodes;
        private int objectsProcessed;
        private ObjectSummaryCollection objectSummaries;
        private ProgressDialog progDialog;
        //private DialogResult progDialogResult;
        private Ait.Services.ReleaseState releaseStateService;
        private TreeNode rootNode;
        private Ait.Services.Topic topicService;
        private int totalNodes;
        private Ait.Services.Variable variableService;
        private string wordCountRegEx;
        private AuthoritEventArgs clickArgs;

        #endregion Fields

        #region Constructors (1)

        public PluginManager(AitApplicationProxy Proxy)
        {
            htmlRegex = new Regex(Settings.Default.HtmlRegex, RegexOptions.Compiled);
            appProxy = Proxy;
            ContextMenuAddArgs contextArgs = new ContextMenuAddArgs();
            contextArgs.PluginName = Resources.PluginNameContext;
            contextArgs.SelectedObjectsMin = 1;
            contextArgs.SelectedObjectsSameType = false;
            contextArgs.OnClick = clickHandlerContext;
            wordCountRegEx = Settings.Default.WordCountRegularExpression;
            contextArgs.IconImage = Resources.ShowDependencies;
            Proxy.AddPluginToObjectContextMenu(contextArgs);
            bgWorker.WorkerReportsProgress = true;
            bgWorker.WorkerSupportsCancellation = true;
            bgWorker.DoWork += bw_DoWork;
        }

        #endregion Constructors

        #region Methods (14)

        // Private Methods (14) 

        private void bw_DoWork(object sender, DoWorkEventArgs e)
        {
            BackgroundWorker bw = sender as BackgroundWorker;
            TreeNode arg = (TreeNode)e.Argument;

            if (bw.CancellationPending == true)
            {
                e.Cancel = true;
            }
        }

        private void AddNodes(ref TreeNode parentNode)
        {
            int bookId = -1;
            if (parentNode.Tag != null)
            {
                bookId = ((ObjectSummary)parentNode.Tag).InBookId;
            }
            ObjectSummary parentObject = (ObjectSummary)parentNode.Tag;
            progDialog.SetLabelProgressMessage(parentObject.Description, parentObject.ObjectId);
            bool inBranch = false;
            TreeNode checkNode = parentNode;
            Stack<int> idInBranch = new Stack<int>();
            idInBranch.Push(((ObjectSummary)parentNode.Tag).ObjectId);
            while (checkNode.Level > 0)
            {
                checkNode = checkNode.Parent;
                if (idInBranch.Contains(((ObjectSummary)checkNode.Tag).ObjectId))
                {
                    inBranch = true;
                }
                idInBranch.Push(((ObjectSummary)checkNode.Tag).ObjectId);
            }
            if (!inBranch)
            {
                List<Ait.Dto.ObjectBrief> children = (List<Ait.Dto.ObjectBrief>)objectCommon.GetRelated(parentObject.ObjectId, Ait.Dto.ObjectRelationship.Uses);
                for (int i = 0; i < children.Count; i++)
                {
                    ObjectSummary childObject = new ObjectSummary(children[i]);
                    progDialog.SetLabelProgressMessage(childObject.Description, childObject.ObjectId);
                    TreeNode childNode = new TreeNode();
                    bool inTree = false;
                    TreeNode[] foundNodes = rootNode.Nodes.Find(childObject.ObjectId.ToString(), true);
                    TreeNode cachedNode = new TreeNode();
                    int j = 0;
                    while (j < foundNodes.Length && !inTree)
                    {
                        if (((ObjectSummary)foundNodes[j].Tag).ObjectId == childObject.ObjectId)
                        {
                            inTree = true;
                            cachedNode = (TreeNode)foundNodes[j].Clone();
                        }
                    }
                    if (!inTree)
                    {
                        if (childObject.ObjectType == Ait.Dto.ObjectType.Book)
                        {
                            childObject.InBookId = childObject.ObjectId;
                            childObject.TopicText = String.Empty;
                        }
                        else
                        {
                            childObject.InBookId = parentObject.InBookId;
                        }
                        if (childObject.ObjectType == Ait.Dto.ObjectType.Topic)
                        {
                            childObject.WordCountResolved = GetWordCount(childObject.ObjectId, childObject.InBookId);
                            childObject.WordCountUnresolved = GetWordCount(childObject.ObjectId);
                            childObject.TopicText = GetObjectSummaryTopicText(childObject.ObjectId);
                        }
                        else
                        {
                            childObject.TopicText = String.Empty;
                        }
                        childObject.ReleaseStateName = GetReleaseStateName(childObject.ReleaseStateId);
                        SetReleaseStateColors(ref childObject);
                        FormatTreeNode(childObject, ref childNode);
                        if (!objectSummaries.Contains(childObject.ObjectId))
                        {
                            objectSummaries.Add(childObject);
                        }
                    }
                    else
                    {
                        childNode = cachedNode;
                    }
                    if (parentNode.Nodes.Find(childNode.Name,false).Length < 1)
                    {
                        parentNode.Nodes.Add(childNode);
                        AddNodes(ref childNode);
                    }
                }
            }
            else
            {
                parentNode.Text = String.Format(Resources.CircularReferenceNodeName, parentNode.Text);
                StringBuilder sb = new StringBuilder(parentNode.ToolTipText);
                sb.AppendLine();
                sb.AppendLine(Resources.CircularReferenceNodeTooltip);
                parentNode.ToolTipText = sb.ToString();
            }
        }

        private string GetObjectSummaryTopicText(int topicId)
        {
            String topicText = String.Empty;
            String topicTextXml = topicService.GetTopicTextXml(topicId);
            if (!String.IsNullOrEmpty(topicTextXml))
            {
                topicText = topicTextXml;
            }
            return topicText;
        }

        private void clickHandlerContext(object sender, AuthoritEventArgs args)
        {
            System.DateTime startTime = System.DateTime.Now;
            if (Settings.Default.GridColumnHeaders.Count != Settings.Default.GridColumnNames.Count)
            {
                MessageBox.Show(String.Format(Resources.UnequalHeadersAndNamesDialogMessage, Settings.Default.GridColumnHeaders.Count, Settings.Default.GridColumnNames.Count), Resources.InvalidSettingDialogTitle, MessageBoxButtons.OK, MessageBoxIcon.Stop);
                return;
            }
            clickArgs = args;
            objectsProcessed = 0;
            totalNodes = 0;
            appContext = new Ait.ApplicationContext(AitConfig.PluginUtility.GetLibraryConfigurationList(clickArgs));
            objectCommon = appContext.GetServiceInstance<Ait.Services.ObjectCommon>();
            releaseStateService = appContext.GetServiceInstance<Ait.Services.ReleaseState>();
            authService = appContext.GetServiceInstance<Authorit.API.Services.Authentication>();
            topicService = appContext.GetServiceInstance<Authorit.API.Services.Topic>();
            variableService = appContext.GetServiceInstance<Authorit.API.Services.Variable>();
            objectSummaries = new ObjectSummaryCollection();
            objectNodes = new List<TreeNode>();
            InitializeProgressDialog();

            Authorit.API.Dto.LoginResponse loginResponse = new Ait.Dto.LoginResponse();
            if (!authService.IsLoggedIn())
            {
                loginResponse = authService.Login(appContext.LibrariesConfiguration[0].Alias, clickArgs.Username, clickArgs.Password);
                switch (loginResponse.Result)
                {
                    case Authorit.API.Dto.LoginResult.FailureDatabaseNotFound:
                        MessageBox.Show(Properties.Resources.MessageDatabaseNotFoundError, Properties.Resources.MessageLoginErrorCaption, MessageBoxButtons.OK, MessageBoxIcon.Error);
                        break;
                    case Authorit.API.Dto.LoginResult.FailureError:
                        MessageBox.Show(Properties.Resources.MessageLoginFailureError, Properties.Resources.MessageLoginErrorCaption, MessageBoxButtons.OK, MessageBoxIcon.Error);
                        break;
                    case Authorit.API.Dto.LoginResult.FailureIncorrectUsernameOrPassword:
                        MessageBox.Show(Properties.Resources.MessageLoginError, Properties.Resources.MessageLoginErrorCaption, MessageBoxButtons.OK, MessageBoxIcon.Error);
                        break;
                    case Authorit.API.Dto.LoginResult.FailureLibraryFutureVersion:
                        MessageBox.Show(Properties.Resources.MessageLibraryFutureVersionError, Properties.Resources.MessageLoginErrorCaption, MessageBoxButtons.OK, MessageBoxIcon.Error);
                        break;
                    case Authorit.API.Dto.LoginResult.FailureLibraryOpen:
                        MessageBox.Show(Properties.Resources.MessageLibraryOpenError, Properties.Resources.MessageLoginErrorCaption, MessageBoxButtons.OK, MessageBoxIcon.Error);
                        break;
                    case Authorit.API.Dto.LoginResult.FailureLibraryUpgradeNeeded:
                        MessageBox.Show(Properties.Resources.MessageLibraryUpgradeRequiredError, Properties.Resources.MessageLoginErrorCaption, MessageBoxButtons.OK, MessageBoxIcon.Error);
                        break;
                    case Authorit.API.Dto.LoginResult.FailureLicensing:
                        MessageBox.Show(Properties.Resources.MessageLicenseError, Properties.Resources.MessageLoginErrorCaption, MessageBoxButtons.OK, MessageBoxIcon.Error);
                        break;
                    case Authorit.API.Dto.LoginResult.Success:
                        //bgWorker.RunWorkerAsync();
                        DialogResult result = MessageBox.Show(Properties.Resources.ConfirmDialogMessage, Properties.Resources.ConfirmDialogCaption, MessageBoxButtons.YesNo, MessageBoxIcon.Warning, MessageBoxDefaultButton.Button1);
                        if (result == DialogResult.Yes)
                        {
                            progDialog.Show();
                            rootNode = new TreeNode("ROOT NODE");
                            rootNode.Tag = new ObjectSummary();
                            List<Authorit.API.Dto.ObjectBrief> selectedObjects = (List<Authorit.API.Dto.ObjectBrief>)objectCommon.GetObjectBriefs(args.SelectedObjectIDs);
                            for (int i = 0; i < selectedObjects.Count; i++)
                            {
                                ObjectSummary selectedObject = new ObjectSummary(selectedObjects[i]);
                                if (selectedObject.ObjectType == Ait.Dto.ObjectType.Book)
                                {
                                    selectedObject.InBookId = selectedObject.ObjectId;
                                    selectedObject.TopicText = String.Empty;
                                }
                                if (selectedObject.ObjectType == Ait.Dto.ObjectType.Topic)
                                {
                                    selectedObject.WordCountResolved = GetWordCount(selectedObject.ObjectId, selectedObject.InBookId);
                                    selectedObject.WordCountUnresolved = GetWordCount(selectedObject.ObjectId);
                                    selectedObject.TopicText = GetObjectSummaryTopicText(selectedObject.ObjectId);
                                }
                                else
                                {
                                    selectedObject.TopicText = String.Empty;
                                }
                                selectedObject.ReleaseStateName = GetReleaseStateName(selectedObject.ReleaseStateId);
                                SetReleaseStateColors(ref selectedObject);
                                TreeNode newNode = new TreeNode();
                                FormatTreeNode(selectedObject, ref newNode);
                                rootNode.Nodes.Add(newNode);
                                AddNodes(ref newNode);
                            }

                            for (int i = 0; i < rootNode.Nodes.Count; i++)
                            {
                                objectNodes.Add(rootNode.Nodes[i]);
                            }
                            progDialog.Close();
                            progDialog.Dispose();
                            System.DateTime endTime = System.DateTime.Now;
                            frmShowDependencies mainForm = new frmShowDependencies(this);
                            mainForm.EventArgs = args;
                            mainForm.TreeNodes = objectNodes;
                            mainForm.UniqueObjects = objectSummaries;
                            mainForm.StartTime = startTime;
                            mainForm.EndTime = endTime;
                            mainForm.ShowDialog();
                        }
                        break;
                    default:
                        break;
                }
            }

        }

		public string GetXml(IList<int> objectIds)
		{
			if (objectIds.Count > 0)
			{
				return objectCommon.GetXmlForObjects(objectIds.ToList());
			}
			else
			{
				return String.Empty;
			}
		}

        private void SetReleaseStateColors(ref ObjectSummary objSummary)
        {
            Ait.Dto.ReleaseState relState = releaseStateService.Get(objSummary.ReleaseStateId);
            if (relState != null)
            {
				//TODO: Support background color?
                //objSummary.BackgroundColor = relState.BackgroundColor;
                objSummary.ForegroundColor = relState.ForegroundColor;
            }
        }

        private void FormatTreeNode(ObjectSummary objSummary, ref TreeNode node)
        {
            node.Name = objSummary.ObjectId.ToString();
            node.Text = objSummary.Description;
            //TODO: Implement a way to indicate variants and variant parents
            //if (objSummary.IsVariantParent)
            //{
            //    node.NodeFont = Settings.Default.TreeViewVariantParentFont;
            //    node.ForeColor = Settings.Default.TreeViewVariantParentForeColor;
            //    node.BackColor = Settings.Default.TreeViewVariantParentBackColor;
            //}
            if (objSummary.ForegroundColor != null)
            {
                node.ForeColor = objSummary.ForegroundColor;
            }
            else
            {
                node.ForeColor = Settings.Default.TreeViewForeColor;
            }
            if (objSummary.BackgroundColor != null)
            {
                node.BackColor = objSummary.BackgroundColor;
            }
            else
            {
                node.BackColor = Settings.Default.TreeViewBackColor;
            }
            node.ImageKey = objSummary.ObjectType.ToString();
            node.SelectedImageKey = objSummary.ObjectType.ToString();
            StringBuilder tooltipText = new StringBuilder();
            StringBuilder toolTipText = new StringBuilder();
            tooltipText.AppendFormat(Resources.ObjectDescriptionTooltip, objSummary.Description);
            tooltipText.AppendLine();
            tooltipText.AppendFormat(Resources.ObjectIdTooltip, objSummary.ObjectId);
            tooltipText.AppendLine();
            tooltipText.AppendFormat(Resources.ObjectTypeTooltip, objSummary.ObjectType);
            tooltipText.AppendLine();
            tooltipText.AppendFormat(Resources.ObjectReleaseStateTooltip, objSummary.ReleaseStateName);
            tooltipText.AppendLine();
            tooltipText.AppendFormat(Resources.ObjectLastUpdateTooltip, objSummary.ModifiedDate, objSummary.ModifiedBy);
            node.Tag = objSummary;
            node.ToolTipText = tooltipText.ToString();
        }

        private string GetReleaseStateName(int releaseStateId)
        {
            string releaseStateName = "";
            Authorit.API.Dto.ReleaseState relState = releaseStateService.Get(releaseStateId);
            if (null != relState)
            {
                releaseStateName = relState.Name;
            }
            else
            {
                releaseStateName = Resources.ReleaseStateNullName;
            }
            return releaseStateName;
        }

        private string GetCleanedTopicText(string topicTextXml)
        {
            string topicText = String.Empty;
            topicText = htmlRegex.Replace(topicTextXml, String.Empty);
            return topicText;
        }

        private int GetWordCount(string text)
        {
            MatchCollection matchesCollection = Regex.Matches(text, wordCountRegEx);
            return matchesCollection.Count;
        }

        private int GetWordCount(int topicId)
        {
            string textXml = topicService.GetTopicTextXml(topicId);
            string topicText = GetCleanedTopicText(textXml);
            return GetWordCount(topicText);
        }

        private int GetWordCount(int topicId, int bookId)
        {
            string topicText = topicService.GetResolvedTopicTextXml(topicId, bookId);
            topicText = GetCleanedTopicText(topicText);
            return GetWordCount(topicText);
        }

        private void InitializeProgressDialog()
        {
            progDialog = new ProgressDialog();
            //progDialogResult = new DialogResult();
        }

        private bool IsBook(Authorit.API.Dto.ObjectBrief objBrief)
        {
            bool isBook = false;
            if (objBrief.ObjectType == Ait.Dto.ObjectType.Book)
            {
                isBook = true;
            }
            return isBook;
        }

        private bool IsInTree(ref TreeNode nodeToCheck)
        {
            bool inTree = false;
            TreeNode parentNode = nodeToCheck.Parent;
            while (parentNode != null)
            {
                if (parentNode.Tag.Equals(nodeToCheck.Tag))
                {
                    inTree = true;
                }
            }
            return inTree;
        }

        private void SetProgressPercentage()
        {
            int percentComplete = 0;
            totalNodes = rootNode.GetNodeCount(true);
            if (totalNodes < 1)
            {
                totalNodes++;
            }
            percentComplete = (objectsProcessed / totalNodes);
        }

        private void UpdateProgressDialog(string description, int id)
        {
            progDialog.SetLabelProgressMessage(description, id);
        }

        #endregion Methods
    }
}
